Completed
Push — master ( 51b9e4...c7e97b )
by Rain
01:27
created

PgpUserStore.findPrivateKeysByEncryptionKeyIds   A

Complexity

Conditions 4
Paths 9

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
c 1
b 0
f 0
nc 9
nop 3
dl 0
loc 21
rs 9.0534

2 Functions

Rating   Name   Duplication   Size   Complexity  
A 0 1 1
B 0 4 5
1
2
var
3
	_ = require('_'),
4
	ko = require('ko'),
5
	$ = require('$'),
6
	kn = require('Knoin/Knoin'),
7
8
	Translator = require('Common/Translator'),
9
	Settings = require('Storage/Settings'),
10
11
	Utils = require('Common/Utils');
12
13
/**
14
 * @constructor
15
 */
16
function PgpUserStore()
17
{
18
	this.capaOpenPGP = ko.observable(false);
19
20
	this.openpgp = null;
21
22
	this.openpgpkeys = ko.observableArray([]);
23
	this.openpgpKeyring = null;
24
25
	this.openpgpkeysPublic = this.openpgpkeys.filter(function(oItem) {
26
		return !!(oItem && !oItem.isPrivate);
27
	});
28
29
	this.openpgpkeysPrivate = this.openpgpkeys.filter(function(oItem) {
30
		return !!(oItem && oItem.isPrivate);
31
	});
32
}
33
34
/**
35
 * @returns {boolean}
36
 */
37
PgpUserStore.prototype.isSupported = function()
38
{
39
	return !!this.openpgp;
40
};
41
42
PgpUserStore.prototype.findKeyByHex = function(aKeys, sHash)
43
{
44
	return _.find(aKeys, function(oItem) {
45
		return sHash && oItem && (sHash === oItem.id || -1 < oItem.ids.indexOf(sHash));
46
	});
47
};
48
49
PgpUserStore.prototype.findPublicKeyByHex = function(sHash)
50
{
51
	return this.findKeyByHex(this.openpgpkeysPublic(), sHash);
52
};
53
54
PgpUserStore.prototype.findPrivateKeyByHex = function(sHash)
55
{
56
	return this.findKeyByHex(this.openpgpkeysPrivate(), sHash);
57
};
58
59
PgpUserStore.prototype.findPublicKeysByEmail = function(sEmail)
60
{
61
	return _.compact(_.flatten(_.map(this.openpgpkeysPublic(), function(oItem) {
62
		var oKey = oItem && -1 < oItem.emails.indexOf(sEmail) ? oItem : null;
63
		return oKey ? oKey.getNativeKeys() : [null];
64
	}), true));
65
};
66
67
PgpUserStore.prototype.findPublicKeysBySigningKeyIds = function(aSigningKeyIds)
68
{
69
	var self = this;
70
	return _.compact(_.flatten(_.map(aSigningKeyIds, function(oId) {
71
		var oKey = oId && oId.toHex ? self.findPublicKeyByHex(oId.toHex()) : null;
72
		return oKey ? oKey.getNativeKeys() : [null];
73
	}), true));
74
};
75
76
PgpUserStore.prototype.findPrivateKeysByEncryptionKeyIds = function(aEncryptionKeyIds, aRecipients, bReturnWrapKeys)
77
{
78
	var
79
		self = this,
80
		aResult = Utils.isArray(aEncryptionKeyIds) ? _.compact(_.flatten(_.map(aEncryptionKeyIds, function(oId) {
81
			var oKey = oId && oId.toHex ? self.findPrivateKeyByHex(oId.toHex()) : null;
82
			return oKey ? (bReturnWrapKeys ? [oKey] : oKey.getNativeKeys()) : [null];
83
		}), true)) : [];
84
85
	if (0 === aResult.length && Utils.isNonEmptyArray(aRecipients))
86
	{
87
		aResult = _.uniq(_.compact(_.flatten(_.map(aRecipients, function(sEmail) {
88
			var aKeys = sEmail ? self.findAllPrivateKeysByEmailNotNative(sEmail) : null;
89
			return aKeys ? (bReturnWrapKeys ? aKeys : _.flatten(_.map(aKeys, function(oKey) {
90
				return oKey.getNativeKeys();
91
			}), true)) : [null];
92
		}), true)), function(oKey) {return oKey.id;});
93
	}
94
95
	return aResult;
96
};
97
98
/**
99
 * @param {string} sEmail
100
 * @returns {?}
101
 */
102
PgpUserStore.prototype.findPublicKeyByEmailNotNative = function(sEmail)
103
{
104
	return _.find(this.openpgpkeysPublic(), function(oItem) {
105
		return oItem && -1 < oItem.emails.indexOf(sEmail);
106
	}) || null;
107
};
108
109
/**
110
 * @param {string} sEmail
111
 * @returns {?}
112
 */
113
PgpUserStore.prototype.findPrivateKeyByEmailNotNative = function(sEmail)
114
{
115
	return _.find(this.openpgpkeysPrivate(), function(oItem) {
116
		return oItem && -1 < oItem.emails.indexOf(sEmail);
117
	}) || null;
118
};
119
120
/**
121
 * @param {string} sEmail
122
 * @returns {?}
123
 */
124
PgpUserStore.prototype.findAllPublicKeysByEmailNotNative = function(sEmail)
125
{
126
	return _.filter(this.openpgpkeysPublic(), function(oItem) {
127
		return oItem && -1 < oItem.emails.indexOf(sEmail);
128
	}) || null;
129
};
130
131
/**
132
 * @param {string} sEmail
133
 * @returns {?}
134
 */
135
PgpUserStore.prototype.findAllPrivateKeysByEmailNotNative = function(sEmail)
136
{
137
	return _.filter(this.openpgpkeysPrivate(), function(oItem) {
138
		return oItem && -1 < oItem.emails.indexOf(sEmail);
139
	}) || null;
140
};
141
142
/**
143
 * @param {string} sEmail
144
 * @param {string=} sPassword
145
 * @returns {?}
146
 */
147
PgpUserStore.prototype.findPrivateKeyByEmail = function(sEmail, sPassword)
148
{
149
	var
150
		oPrivateKey = null,
151
		oKey = _.find(this.openpgpkeysPrivate(), function(oItem) {
152
			return oItem && -1 < oItem.emails.indexOf(sEmail);
153
		});
154
155
	if (oKey)
156
	{
157
		try
158
		{
159
			oPrivateKey = oKey.getNativeKeys()[0] || null;
160
			if (oPrivateKey)
161
			{
162
				oPrivateKey.decrypt(Utils.pString(sPassword));
163
			}
164
		}
165
		catch (e)
166
		{
167
			oPrivateKey = null;
168
		}
169
	}
170
171
	return oPrivateKey;
172
};
173
174
/**
175
 * @param {string=} sPassword
176
 * @returns {?}
177
 */
178
PgpUserStore.prototype.findSelfPrivateKey = function(sPassword)
179
{
180
	return this.findPrivateKeyByEmail(require('Stores/User/Account').email(), sPassword);
181
};
182
183
PgpUserStore.prototype.decryptMessage = function(oMessage, aRecipients, fCallback)
184
{
185
	if (oMessage && oMessage.getEncryptionKeyIds)
186
	{
187
		var self = this,
188
			aPrivateKeys = this.findPrivateKeysByEncryptionKeyIds(oMessage.getEncryptionKeyIds(), aRecipients, true);
189
190
		if (aPrivateKeys && 0 < aPrivateKeys.length)
191
		{
192
			kn.showScreenPopup(require('View/Popup/MessageOpenPgp'), [function(oDecryptedKey) {
193
194
				if (oDecryptedKey)
195
				{
196
					oMessage.decrypt(oDecryptedKey).then(function(oDecryptedMessage) {
197
						var oPrivateKey = null;
198
						if (oDecryptedMessage)
199
						{
200
							oPrivateKey = self.findPrivateKeyByHex(oDecryptedKey.primaryKey.keyid.toHex());
201
							if (oPrivateKey)
202
							{
203
								self.verifyMessage(oDecryptedMessage, function(oValidKey, aSigningKeyIds) {
204
									fCallback(oPrivateKey, oDecryptedMessage, oValidKey || null, aSigningKeyIds || null);
205
								});
206
							}
207
							else
208
							{
209
								fCallback(oPrivateKey, oDecryptedMessage);
210
							}
211
						}
212
						else
213
						{
214
							fCallback(oPrivateKey, oDecryptedMessage);
215
						}
216
217
					}, function() {
218
						fCallback(null, null);
219
					});
220
				}
221
				else
222
				{
223
					fCallback(null, null);
224
				}
225
226
			}, aPrivateKeys]);
227
228
			return false;
229
		}
230
	}
231
232
	fCallback(null, null);
233
234
	return false;
235
};
236
237
PgpUserStore.prototype.findKeyExternal = function(sEmail, fCallback)
238
{
239
	if (this.openpgp.HKP && Settings.appSettingsGet('openpgpPublicKeyServer'))
240
	{
241
		var oHkp = new this.openpgp.HKP(Settings.appSettingsGet('openpgpPublicKeyServer').replace(/\/$/, ''));
242
		oHkp.lookup({query: sEmail}).then(function(sKey) {
243
			fCallback(sKey);
244
		}, function() {
245
			fCallback(null);
246
		});
247
	}
248
	else
249
	{
250
		fCallback(null);
251
	}
252
};
253
254
PgpUserStore.prototype.verifyMessage = function(oMessage, fCallback)
255
{
256
	if (oMessage && oMessage.getSigningKeyIds)
257
	{
258
		var aSigningKeyIds = oMessage.getSigningKeyIds();
259
		if (aSigningKeyIds && 0 < aSigningKeyIds.length)
260
		{
261
			var aPublicKeys = this.findPublicKeysBySigningKeyIds(aSigningKeyIds);
262
			if (aPublicKeys && 0 < aPublicKeys.length)
263
			{
264
				try
265
				{
266
					var
267
						aResult = oMessage.verify(aPublicKeys),
268
						oValid = _.find(_.isArray(aResult) ? aResult : [], function(oItem) {
269
							return oItem && oItem.valid && oItem.keyid;
270
						});
271
272
					if (oValid && oValid.keyid && oValid.keyid && oValid.keyid.toHex)
273
					{
274
						fCallback(this.findPublicKeyByHex(oValid.keyid.toHex()));
275
						return true;
276
					}
277
				}
278
				catch (e)
279
				{
280
					Utils.log(e);
281
				}
282
			}
283
284
			fCallback(null, aSigningKeyIds);
285
			return false;
286
		}
287
	}
288
289
	fCallback(null);
290
	return false;
291
};
292
293
/**
294
 * @param {*} mDom
295
 */
296
PgpUserStore.prototype.controlsHelper = function(mDom, oVerControl, bSuccess, sTitle, sText)
297
{
298
	if (bSuccess)
299
	{
300
		mDom.removeClass('error').addClass('success').attr('title', sTitle);
301
		oVerControl.removeClass('error').addClass('success').attr('title', sTitle);
302
	}
303
	else
304
	{
305
		mDom.removeClass('success').addClass('error').attr('title', sTitle);
306
		oVerControl.removeClass('success').addClass('error').attr('title', sTitle);
307
	}
308
309
	if (!Utils.isUnd(sText))
310
	{
311
		mDom.text(Utils.trim(sText.replace(/(\u200C|\u0002)/g, '')));
312
	}
313
};
314
315
/**
316
 * @static
317
 */
318 View Code Duplication
PgpUserStore.domControlEncryptedClickHelper = function(store, mDom, sArmoredMessage, aRecipients)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
319
{
320
	return function() {
321
322
		var
323
			message = null,
324
			$this = $(this);
325
326
		if ($this.hasClass('success'))
327
		{
328
			return false;
329
		}
330
331
		try
332
		{
333
			message = store.openpgp.message.readArmored(sArmoredMessage);
334
		}
335
		catch (e)
336
		{
337
			Utils.log(e);
338
		}
339
340
		if (message && message.getText && message.verify && message.decrypt)
341
		{
342
			store.decryptMessage(message, aRecipients, function(oValidPrivateKey, oDecryptedMessage, oValidPublicKey, aSigningKeyIds) {
343
344
				if (oDecryptedMessage)
345
				{
346
					if (oValidPublicKey)
347
					{
348
						store.controlsHelper(mDom, $this, true, Translator.i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
349
							'USER': oValidPublicKey.user + ' (' + oValidPublicKey.id + ')'
350
						}), oDecryptedMessage.getText());
351
					}
352
					else if (oValidPrivateKey)
353
					{
354
						var
355
							aKeyIds = Utils.isNonEmptyArray(aSigningKeyIds) ? aSigningKeyIds : null,
356
							sAdditional = aKeyIds ? _.compact(_.map(aKeyIds, function(oItem) {
357
								return oItem && oItem.toHex ? oItem.toHex() : null;
358
							})).join(', ') : '';
359
360
						store.controlsHelper(mDom, $this, false,
361
							Translator.i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') +
362
								(sAdditional ? ' (' + sAdditional + ')' : ''),
363
								oDecryptedMessage.getText());
364
					}
365
					else
366
					{
367
						store.controlsHelper(mDom, $this, false,
368
							Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
369
					}
370
				}
371
				else
372
				{
373
					store.controlsHelper(mDom, $this, false,
374
						Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
375
				}
376
			});
377
378
			return false;
379
		}
380
381
		store.controlsHelper(mDom, $this, false, Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
382
		return false;
383
	};
384
};
385
386
/**
387
 * @static
388
 */
389 View Code Duplication
PgpUserStore.domControlSignedClickHelper = function(store, mDom, sArmoredMessage)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
390
{
391
	return function() {
392
393
		var
394
			message = null,
395
			$this = $(this);
396
397
		if ($this.hasClass('success') || $this.hasClass('error'))
398
		{
399
			return false;
400
		}
401
402
		try
403
		{
404
			message = store.openpgp.cleartext.readArmored(sArmoredMessage);
405
		}
406
		catch (e)
407
		{
408
			Utils.log(e);
409
		}
410
411
		if (message && message.getText && message.verify)
412
		{
413
			store.verifyMessage(message, function(oValidKey, aSigningKeyIds) {
414
				if (oValidKey)
415
				{
416
					store.controlsHelper(mDom, $this, true, Translator.i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
417
						'USER': oValidKey.user + ' (' + oValidKey.id + ')'
418
					}), message.getText());
419
				}
420
				else
421
				{
422
					var
423
						aKeyIds = Utils.isNonEmptyArray(aSigningKeyIds) ? aSigningKeyIds : null,
424
						sAdditional = aKeyIds ? _.compact(_.map(aKeyIds, function(oItem) {
425
							return oItem && oItem.toHex ? oItem.toHex() : null;
426
						})).join(', ') : '';
427
428
					store.controlsHelper(mDom, $this, false,
429
						Translator.i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') +
430
							(sAdditional ? ' (' + sAdditional + ')' : ''));
431
				}
432
			});
433
434
			return false;
435
		}
436
437
		store.controlsHelper(mDom, $this, false, Translator.i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
438
		return false;
439
	};
440
};
441
442
/**
443
 * @param {*} mDom
444
 * @param {MessageModel} oRainLoopMessage
445
 */
446
PgpUserStore.prototype.initMessageBodyControls = function(mDom, oRainLoopMessage)
447
{
448
	if (mDom && !mDom.hasClass('inited'))
449
	{
450
		mDom.addClass('inited');
451
452
		var
453
			bEncrypted = mDom.hasClass('encrypted'),
454
			bSigned = mDom.hasClass('signed'),
455
			oVerControl = null,
456
			aRecipients = oRainLoopMessage ? oRainLoopMessage.getEmails(['from', 'to', 'cc']) : [],
457
			sData = '';
458
459
		if (bEncrypted || bSigned)
460
		{
461
			sData = mDom.text();
462
			mDom.data('openpgp-original', sData);
463
464
			if (bEncrypted)
465
			{
466
				oVerControl = $('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
467
					.attr('title', Translator.i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC'))
468
					.on('click', PgpUserStore.domControlEncryptedClickHelper(this, mDom, sData, aRecipients));
469
			}
470
			else if (bSigned)
471
			{
472
				oVerControl = $('<div class="b-openpgp-control"><i class="icon-lock"></i></div>')
473
					.attr('title', Translator.i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC'))
474
					.on('click', PgpUserStore.domControlSignedClickHelper(this, mDom, sData));
475
			}
476
477
			if (oVerControl)
478
			{
479
				mDom.before(oVerControl).before('<div></div>');
480
			}
481
		}
482
	}
483
};
484
485
module.exports = new PgpUserStore();
486